/*=+--+=#=+--     Unmanned Aerial System Management Software      --+=#=+--+=#*\
|          Copyright (C) 2011 Regents of the University of Colorado.           |
|                             All Rights Reserved.                             |

     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License version 2 as
     published by the Free Software Foundation.

     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.

     You should have received a copy of the GNU General Public License
     along with this program.  If not, see <http://www.gnu.org/licenses/>.

            Jack Elston                       Cory Dixon                        
|           elstonj@colorado.edu              dixonc@colorado.edu              |
|                                                                              |
\*=+--+=#=+--                 --+=#=+--+=#=+--                    --+=#=+--+=#*/

#include "antPage.h"
#include "vcConfig.h"

/*<---Extern Variables---->*/
extern GuiStruct guiStr;
extern FifoMsg_t  * fifo;

bool AntPage::create_page(Client *client, GtkBuilder *builder)
{	
	if( !client ) {
		cout << "AntPage::create_page -- invalid client " << endl;
		return false;
	}
	if( page ) {
		cout << "AntPage::create_page -- page has already been created" << endl;
		return true;
	}
	if( !builder ) {
		cout << "AntPage::create_page -- invalid xml builder" << endl;
		return false;
	}

	page_client        = client;
	page               = GTK_WIDGET (gtk_builder_get_object(builder, "antenna_page"));
	node_to_track      = GTK_WIDGET (gtk_builder_get_object(builder, "node_to_track"));
	antenna_track_range   = GTK_WIDGET (gtk_builder_get_object(builder, "antenna_track_range"));
	ant_start_button   = GTK_WIDGET (gtk_builder_get_object(builder, "ant_start_button"));
	ant_stop_button    = GTK_WIDGET (gtk_builder_get_object(builder, "ant_stop_button"));
	ant_home_button    = GTK_WIDGET (gtk_builder_get_object(builder, "ant_home_button"));
	ant_az    = GTK_WIDGET (gtk_builder_get_object(builder, "antenna_azimuth"));
	ant_el    = GTK_WIDGET (gtk_builder_get_object(builder, "antenna_elevation"));

	ant_offset = GTK_WIDGET (gtk_builder_get_object(builder, "antenna_offset"));
	ant_offset_button = GTK_WIDGET (gtk_builder_get_object(builder, "ant_offset_button"));

	// make sure we got all of our widgets
	if( !( page && node_to_track && ant_start_button && ant_stop_button && antenna_track_range && ant_home_button && ant_az && ant_el && ant_offset && ant_offset_button) )
	{
		cout << "AntPage::create_page - did not find all required widgets in xml file" << endl;	
		page = NULL;
		return false;
	}

	g_signal_connect(G_OBJECT(ant_start_button), "clicked", G_CALLBACK(AntPage::ant_start_clicked), this);
	g_signal_connect(G_OBJECT(ant_stop_button), "clicked", G_CALLBACK(AntPage::ant_stop_clicked), this);
	g_signal_connect(G_OBJECT(ant_home_button), "clicked", G_CALLBACK(AntPage::ant_home_clicked), this);

	g_signal_connect(G_OBJECT(ant_offset_button), "clicked", G_CALLBACK(AntPage::ant_offset_clicked), this);

	// range list
	create_node_range_tree();

	return true;
}

void AntPage::update_ant_state() {
	gchar *text;

	text = g_strdup_printf ("%.2f", page_client->antenna_aps.azimuth);
	gtk_entry_set_text (GTK_ENTRY (ant_az), text);
	g_free (text);

	text = g_strdup_printf ("%.2f", page_client->antenna_aps.elevation);
	gtk_entry_set_text (GTK_ENTRY (ant_el), text);
	g_free (text);
}

void AntPage::update()
{
	//-- See if the page has been initialized
	if( !page_client ) {
		cout << "AntPage::update - invalid page_client" << endl;
		return;
	}
	if( page == NULL ) {
		cout << "AntPage::update -- page has not been created" << endl;
		return;
	}

	// don't do work if we don't need too
	if( !GTK_WIDGET_VISIBLE( page ) ) {
		return;
	}

	// update the range list
	update_gps_list();

	update_ant_state();

	// redraw page
	gtk_widget_queue_draw(page);	
}

/* columns for GtkTree */
enum
{
	IND_COLUMN = 0,
	NAME_COLUMN,
	RANGE_COLUMN,
	TRACK_COLUMN,
	NUM_COLUMNS
};

void AntPage::create_node_range_tree()
{
	// create tree store model
	list_store = gtk_list_store_new ( NUM_COLUMNS,
	                                  G_TYPE_INT,     // link ind
	                                  G_TYPE_STRING,  // name
	                                  G_TYPE_STRING,  // range - we convert to string ourselves
	                                  G_TYPE_BOOLEAN  // selected for linking
	                                );
	// draw the view of the tree
	format_treeview();

	// add to container
	gtk_widget_show( GTK_WIDGET(treeview) );
	gtk_container_add( GTK_CONTAINER (node_to_track), GTK_WIDGET(treeview) );
}
	

void AntPage::format_treeview()
{
	gint col_offset;
	GtkCellRenderer *renderer;

	// create tree view 
	treeview = GTK_TREE_VIEW(gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store)));

	// setup tree functionality
	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE);
	gtk_tree_view_set_grid_lines(GTK_TREE_VIEW(treeview), GTK_TREE_VIEW_GRID_LINES_BOTH);
	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), TRUE);
	gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(treeview), TRUE);
	gtk_tree_view_set_reorderable(GTK_TREE_VIEW(treeview), TRUE);
	gtk_tree_view_set_enable_search(GTK_TREE_VIEW(treeview), FALSE);

	GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
	gtk_tree_selection_set_mode( selection, GTK_SELECTION_NONE);

	// column for node slection
	renderer = gtk_cell_renderer_toggle_new ();
	g_object_set (renderer, "xalign", 0.5, NULL);
	g_object_set_data (G_OBJECT (renderer), "column", (gint *)TRACK_COLUMN);
	col_offset = gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
	      -1, "X", renderer, 
	      "active", TRACK_COLUMN,
	      NULL);
	// connect signal for toggeling
	g_signal_connect (renderer, "toggled", G_CALLBACK (AntPage::item_toggled), this);

	// column for node names 
	renderer = gtk_cell_renderer_text_new ();
	g_object_set (renderer, "xalign", 0.0, NULL);
	col_offset = gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
			-1, "Node", renderer, 
			"text", NAME_COLUMN,
			NULL);

	// column for node range
	renderer = gtk_cell_renderer_text_new ();
	g_object_set (renderer, "xalign", 1.0, NULL);
	g_object_set_data (G_OBJECT (renderer), "column", (gint *)RANGE_COLUMN);

	col_offset = gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
			-1, "Range", renderer, 
			"text", RANGE_COLUMN,
			NULL);

}

void AntPage::update_gps_list()
{
	GtkTreeIter iter;
	char idStr[32];
	char rangeStr[32];

	Client * track_client;
	int c_ind = 0;

	// loop through all known clients
	while( (track_client = guiStr.shm_ptr->clients.getClient(c_ind)) ) {

		// make sure the tracked client is not this node
		if(track_client->name != page_client->name) {

			// does the track client have GPS capabilities
			if(track_client->capabilities.find(CMD_TELEM) || track_client->capabilities.find(CMD_GPS_TELEM)) {

				// calculate the range to the target
				double range = page_client->rangeTo( track_client );
				ant_ranges[c_ind] = range;
				sprintf(rangeStr,"%.1f", range);

				//-- fill in the name of the node --//
				strcpy(idStr, track_client->name.c_str());

				//-- walk through the list looking for the link in the tree model
				// if empty, just fill in new value
				bool valid = gtk_tree_model_get_iter_first( GTK_TREE_MODEL(list_store), &iter);
				while( valid ) {
					int link_ind;
					gtk_tree_model_get (GTK_TREE_MODEL(list_store), &iter, IND_COLUMN, &link_ind, -1);
					// if this is the current link
					if( link_ind == c_ind )
						break;

					valid = gtk_tree_model_iter_next ( GTK_TREE_MODEL(list_store), &iter);
				}
				// if we still have a valid iteration, we found the current link
				// already in the model, if not add new one
				if( !valid )
					gtk_list_store_append (list_store, &iter);

				//-- fill in the gtk tree menu --//
				gtk_list_store_set (list_store, &iter,
						IND_COLUMN,  c_ind,
						NAME_COLUMN, idStr,
						RANGE_COLUMN, rangeStr,
						-1);

			} // has gps stream
			//else cout << " NO GPS" << endl;
		} // not me

		// get next client
		c_ind++;
	}

}

void AntPage::item_toggled(GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
{
	AntPage *me = (AntPage *)data;
	if( !me )
		return;

	GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
	GtkTreeIter iter;

	int onOff = 0;

	//cout << "AntPage::item_toggled: path=" << path_str << endl;

	// get toggled iter 
	gtk_tree_model_get_iter (GTK_TREE_MODEL(me->list_store), &iter, path);

	// get data from current row
	gtk_tree_model_get (GTK_TREE_MODEL(me->list_store), &iter, TRACK_COLUMN, &onOff, -1);

	// do something with the value
	onOff ^= 1;

	// set new value 
	gtk_list_store_set (me->list_store, &iter, TRACK_COLUMN, onOff, -1);

	// clean up 
	gtk_tree_path_free (path);
}


void AntPage::ant_start_clicked(GtkButton *button, gpointer data)
{
	//cout << "AntPage::track_nodes_clicked" << endl;
	
	AntPage *track = (AntPage *)data;
	if( !track )
		return;

	track->send_track_cmd();
	track->set_sent_state(GTK_WIDGET(button));

	return;
}

void AntPage::ant_stop_clicked(GtkButton *button, gpointer data)
{
	//cout << "AntPage::track_stop_clicked" << endl;
	
	AntPage *track = (AntPage *)data;
	if( !track )
		return;

	track->send_stop_track_cmd();

	return;
}

void AntPage::ant_home_clicked(GtkButton *button, gpointer data)
{
	cout << "AntPage::track_stop_clicked" << endl;

	AntPage *track = (AntPage *)data;
	if( !track )
		return;

	track->send_launch_cmd();

	return;
}

void AntPage::ant_offset_clicked(GtkButton *button, gpointer data)
{
	//cout << "AntPage::track_stop_clicked" << endl;
	AntPage *track = (AntPage *)data;
	if( !track )
		return;

	track->send_settings_cmd();

	return;
}

void AntPage::send_track_cmd()
{	
	GtkTreeIter iter;

	Client * me       = guiStr.shm_ptr->clients.getClient(0);
	uint8_t * dataPtr = CMD_DATAPTR(fifo->data);

	fifo->src.s_addr = guiStr.shm_ptr->clients.getAddr(me, page_client).s_addr;
	fifo->dest.s_addr = guiStr.shm_ptr->clients.getAddr(page_client).s_addr;

	int payload_size=0;
	int num_track = 0;
	VehicleInfo_t * vehicle;

	gchar * setNames[MAX_CLIENTS];
	void  * setPtr[MAX_CLIENTS];
	int     setType[MAX_CLIENTS];
	
	//-- walk through the list looking for any selected nodes
	bool valid = gtk_tree_model_get_iter_first( GTK_TREE_MODEL(list_store), &iter);
	while( valid ) {
		int selected;
		int link_ind;
		gchar *name;
		gtk_tree_model_get (GTK_TREE_MODEL(list_store), &iter, 
		                    IND_COLUMN, &link_ind, 
	                       TRACK_COLUMN, &selected, 
								  NAME_COLUMN, &name, -1);

		// if this node is selected
		if( selected ) {
			payload_size += sizeof(VehicleInfo_t);
			vehicle = (VehicleInfo_t *)dataPtr;

			strcpy(vehicle->id, name);
			//cout << "\t" << vehicle->id << endl;

			setNames[num_track] = g_strdup_printf ("%s", name);
			setType[num_track]  = FLOAT_T;
			setPtr[num_track]   = &(ant_ranges[link_ind]);

			dataPtr += sizeof(VehicleInfo_t);
			num_track++;
		}
		g_free(name);

		// get next node
		valid = gtk_tree_model_iter_next ( GTK_TREE_MODEL(list_store), &iter);
	}

	if( num_track > 0 ) {
		
		//-- RANGE plot 
		if( range_graph ) 
			gtk_container_remove(GTK_CONTAINER(antenna_track_range), range_graph->main_widget);
		range_graph = NULL;

		String chart_name = page_client->name;
		chart_name += "/";
		chart_name += "RANGE";

		range_graph = gtkstripchart_new(num_track, (const gchar**)setNames, setPtr, setType, chart_name.c_str());
		range_graph->parent = antenna_track_range;

		gtk_container_add (GTK_CONTAINER (antenna_track_range), range_graph->main_widget);
		gtk_container_set_border_width (GTK_CONTAINER (range_graph->main_widget), GNOME_PAD_SMALL);
		gtkstripchart_start (range_graph);

		//-- send CMD_TRACK
		CMD_CMD(fifo->data)  = CMD_TRACK;
		CMD_NUM(fifo->data)  = payload_size;
		CMD_CSUM(fifo->data) = 0;
		CMD_CSUM(fifo->data) = C_calcCheckSum((uint8_t*)fifo->data);

		fifo->size = CMD_SIZE(fifo->data);
		guiStr.vcgui->writeFifo(ThreadedPipe::INBOARD_TX);

		//printf("<-- Sent %s CMD_TRACK -->\n", page_client->name.c_str());	
	}

	// free memory
	for( int i=0; i < num_track; i++)
		g_free ( setNames[i] );
}

void AntPage::send_stop_track_cmd()
{	
	Client * me       = guiStr.shm_ptr->clients.getClient(0);

	fifo->src.s_addr = guiStr.shm_ptr->clients.getAddr(me, page_client).s_addr;
	fifo->dest.s_addr = guiStr.shm_ptr->clients.getAddr(page_client).s_addr;

	CMD_CMD(fifo->data)  = CMD_TRACK;
	CMD_NUM(fifo->data)  = 0;
	CMD_CSUM(fifo->data) = 0;
	CMD_CSUM(fifo->data) = C_calcCheckSum((uint8_t*)fifo->data);

	fifo->size = CMD_SIZE(fifo->data);
	guiStr.vcgui->writeFifo(ThreadedPipe::INBOARD_TX);

	//-- RANGE plot 
	if( range_graph ) 
		gtk_container_remove(GTK_CONTAINER(antenna_track_range), range_graph->main_widget);
	range_graph = NULL;

	//printf("<-- Sent %s CMD_TRACK -- STOP -->\n", page_client->name.c_str());	
}

void AntPage::send_launch_cmd()
{	
	Client * me       = guiStr.shm_ptr->clients.getClient(0);

	fifo->src.s_addr = guiStr.shm_ptr->clients.getAddr(me, page_client).s_addr;
	fifo->dest.s_addr = guiStr.shm_ptr->clients.getAddr(page_client).s_addr;

	AntState_t * settings = (AntState_t *)(CMD_DATAPTR(fifo->data));
	settings->launch_state = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(ant_home_button)); 

	CMD_CMD(fifo->data)  = CMD_ANT_STATE;
	CMD_NUM(fifo->data)  = sizeof(AntState_t);
	CMD_CSUM(fifo->data) = 0;
	CMD_CSUM(fifo->data) = C_calcCheckSum((uint8_t*)fifo->data);

	fifo->size = CMD_SIZE(fifo->data);
	guiStr.vcgui->writeFifo(ThreadedPipe::INBOARD_TX);

	set_sent_state(GTK_WIDGET(ant_home_button));
}

void AntPage::send_settings_cmd()
{	
	Client * me       = guiStr.shm_ptr->clients.getClient(0);

	fifo->src.s_addr = guiStr.shm_ptr->clients.getAddr(me, page_client).s_addr;
	fifo->dest.s_addr = guiStr.shm_ptr->clients.getAddr(page_client).s_addr;

	AntSettings_t * settings = (AntSettings_t *)(CMD_DATAPTR(fifo->data));

	const gchar *valStr = gtk_entry_get_text( GTK_ENTRY(ant_offset) );
	int val = atoi(valStr);
	settings->offset = val;

	CMD_CMD(fifo->data)  = CMD_ANT_SETTINGS;
	CMD_NUM(fifo->data)  = sizeof(AntSettings_t);
	CMD_CSUM(fifo->data) = 0;
	CMD_CSUM(fifo->data) = C_calcCheckSum((uint8_t*)fifo->data);

	fifo->size = CMD_SIZE(fifo->data);
	guiStr.vcgui->writeFifo(ThreadedPipe::INBOARD_TX);

	set_sent_state(GTK_WIDGET(ant_offset_button));
}

// use this function to indicate a command or ACK has been received
void AntPage::setCMD_RX(uint8_t cmd_id, int ack_nack)
{
	//cout << "AntPage::setCMD_RX - [" << (int)cmd_id << "]  ackNack=" << ack_nack << endl;
	
	switch( cmd_id ) {
		case CMD_TRACK:
			set_rx_state(ant_start_button, ack_nack);
			break;
		case CMD_ANT_SETTINGS:
			set_rx_state(ant_offset_button, ack_nack);
			break;
		case CMD_ANT_STATE:
			set_rx_state(ant_home_button, ack_nack);
			break;

		default:
			break;
	}

	// update page
	if( page )
		gtk_widget_queue_draw(page);
}
